package org.lestr.secuel.noyau.sauvegarde;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipInputStream;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

import org.lestr.secuel.m3ds.Univers;
import org.lestr.secuel.m3ds.Usine;
import org.lestr.secuel.m3ds.sauvegarde.M3DSLecteur;
import org.lestr.secuel.m3ds.sauvegarde.M3DSLectureException;
import org.lestr.secuel.noyau.Jeu;
import org.lestr.secuel.noyau.Scene;
import org.lestr.secuel.noyau.systemeLogique.Condition;
import org.lestr.secuel.noyau.systemeLogique.Effet;
import org.lestr.secuel.noyau.systemeLogique.Regle;
import org.lestr.secuel.noyau.systemeLogique.SystemeLogique;


public class NoyauLecteur {
	
	
	// Définition de constantes
	public static final Namespace espaceDeNommage = Namespace.getNamespace("scl", "secuel::noyau");
	
	
	// Déclaration des attributs
	private Usine usine;
	private SystemeLogique systèmeLogique;
	private M3DSLecteur lecteurM3DS;
	
	
	/** Constructeur */
	public NoyauLecteur(){
		
		// Instanciation des attributs
		usine = null;
		systèmeLogique = null;
		lecteurM3DS = new M3DSLecteur();
		
	}// FIN Constructeur
	
	
	// Accesseurs
	public Usine getUsine() {
		return usine;
	}
	public void setUsine(Usine usine) {
		this.usine = usine; lecteurM3DS.setUsine(usine);
	}
	
	
	/** Lit un jeu */
	public static Jeu lireJeu(File fichier, Usine usine) throws M3DSLectureException {
		
		// Variables locales
		Document document = null;
		
		// Ouverture du document
		try {
			
			// Récupère le flux compressé
			ZipInputStream flux = new ZipInputStream(new FileInputStream(fichier));
			flux.getNextEntry();
			
			// Ouvre le document
			document = new SAXBuilder().build(flux);
			
			// Ferme le flux
			flux.closeEntry();
			flux.close();
			
		} catch (JDOMException e) {} 
		catch (IOException e) {}
		
		// Compile le jeu
		NoyauLecteur lecteur = new NoyauLecteur();
		lecteur.setUsine(usine);
		Jeu rslt = lecteur.compilerJeu(document.getRootElement());
		
		// Retourne le résultat
		return rslt;
		
	}// FIN lireJeu


	/** Lit une scène */
	public static Scene lireScène(File fichier, Usine usine) throws M3DSLectureException {
		
		// Variables locales
		Document document = null;
		
		// Ouverture du document
		try {
			
			// Récupère le flux compressé
			ZipInputStream flux = new ZipInputStream(new FileInputStream(fichier));
			flux.getNextEntry();
			
			// Ouvre le document
			document = new SAXBuilder().build(flux);
			
			// Ferme le flux
			flux.closeEntry();
			flux.close();
			
		} catch (JDOMException e) {} 
		catch (IOException e) {}
		
		// Compile la scène
		NoyauLecteur lecteur = new NoyauLecteur();
		lecteur.setUsine(usine);
		Scene rslt = lecteur.compilerScène(document.getRootElement());
		
		// Retourne le résultat
		return rslt;
		
	}// FIN lireScène
	
	
	/** Compile un jeu */
	private Jeu compilerJeu(Element élément) throws M3DSLectureException {
		
		// Variables locales
		Jeu rslt = new Jeu();
		
		// Mémorise les paramètres généraux
		rslt.setNom(élément.getAttributeValue("nom", espaceDeNommage));

		// Parcours les scènes
		for(Object elementScène : élément.getChildren("scène", espaceDeNommage))
			rslt.ajoutScène(compilerScène((Element) elementScène)); 

		// Retourne le résultat
		return rslt;
		
	}// FIN compilerScène
	
	
	/** Compile une scène */
	private Scene compilerScène(Element élément) throws M3DSLectureException {
		
		// Variables locales
		Scene rslt = null;
		
		// Compile l'univers de la scène
		Univers univers = lecteurM3DS.compilerUnivers(élément.getChild("univers", M3DSLecteur.espaceDeNommage));
		
		// Instancie la scène
		rslt = new Scene(univers);
		
		// Mémorise les paramètres généraux
		rslt.setNom(élément.getAttributeValue("nom", espaceDeNommage));
		
		// Compile le système logique
		compilerSystèmeLogique(élément.getChild("systèmeLogique", espaceDeNommage), rslt.getSystèmeLogique());
		
		// Retourne le résultat
		return rslt;
		
	}// FIN compilerScène
	
	
	/** Compile un systèmeLogique */
	private SystemeLogique compilerSystèmeLogique(Element élément, SystemeLogique rslt) throws M3DSLectureException {
		
		// Mémorise le système logique comme système logique courant
		this.systèmeLogique = rslt;
		
		// Parcours ses règles
		for(Object élémentRègle : élément.getChildren("règle", espaceDeNommage)){
			
			// Mémorise la règle courante
			rslt.ajoutRègle(compilerRègle((Element) élémentRègle));
			
		}
		
		// Retourne le résultat
		return rslt;
		
	}// FIN compilerSystèmeLogique
	
	
	/** Compile une règle */
	private Regle compilerRègle(Element élément) throws M3DSLectureException {
		
		// Variables locales
		Regle rslt = new Regle();
		
		// Mémorise les paramètres généraux
		rslt.setNom(élément.getAttributeValue("nom", espaceDeNommage));
		
		// Mémorise les conditions
		for(Object conditionXML : élément.getChildren("condition", espaceDeNommage))
			rslt.ajoutCondition(compilerCondition((Element) conditionXML));
		
		// Mémorise les effets
		for(Object effetXML : élément.getChildren("effet", espaceDeNommage))
			rslt.ajoutEffet(compilerEffet((Element) effetXML));
		
		// Retourne le résultat
		return rslt;
		
	}// FIN compilerRègle
	
	
	/** Compile une condition */
	private Condition compilerCondition(Element élément) throws M3DSLectureException {
		
		// Variables locales
		Condition rslt = null;
		
		// Instancie la condition
		try {
			rslt = (Condition) Class.forName(élément.getAttributeValue("classe", espaceDeNommage)).newInstance();
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (ClassNotFoundException e) {
		} catch (ClassCastException e) {
		}
		
		// Mémorise les paramètres généraux
		rslt.setNom(élément.getAttributeValue("nom", espaceDeNommage));
		rslt.setSystèmeLogique(systèmeLogique);
		rslt.charger(élément);
		
		// Retourne le résultat
		return rslt;
		
	}// FIN compilerCondition
	
	
	/** Compile un effet */
	private Effet compilerEffet(Element élément) throws M3DSLectureException {
		
		// Variables locales
		Effet rslt = null;
		
		// Instancie la condition
		try {
			rslt = (Effet) Class.forName(élément.getAttributeValue("classe", espaceDeNommage)).newInstance();
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (ClassNotFoundException e) {
		} catch (ClassCastException e) {
		}
		
		// Mémorise les paramètres généraux
		rslt.setNom(élément.getAttributeValue("nom", espaceDeNommage));
		rslt.setSystèmeLogique(systèmeLogique);
		rslt.charger(élément);
		
		// Retourne le résultat
		return rslt;
		
	}// FIN compilerEffet
	

}// FIN Classe
